#include "stdafx.h"
#include "stream/binaryreader.hpp"
#include <algorithm>

namespace JHCPP
{
	namespace stream
	{
		CBinaryReader::CBinaryReader(std::istream& istr, StreamByteOrder byteOrder) : m_istr(istr), m_pTextConverter(0)
		{
		#if defined(JH_ARCH_BIG_ENDIAN)
			m_flipBytes = (byteOrder == LITTLE_ENDIAN_BYTE_ORDER);
		#else
			m_flipBytes = (byteOrder == BIG_ENDIAN_BYTE_ORDER);
		#endif
		}

		CBinaryReader::CBinaryReader(std::istream& istr, CTextEncode& encode, StreamByteOrder byteOrder)
		: m_istr(istr),
		m_pTextConverter(new CTextConverter(encode, CTextEncode::global()))
		{
		#if defined(JH_ARCH_BIG_ENDIAN)
			m_flipBytes = (byteOrder == LITTLE_ENDIAN_BYTE_ORDER);
		#else
			m_flipBytes = (byteOrder == BIG_ENDIAN_BYTE_ORDER);
		#endif
		}

		CBinaryReader::~CBinaryReader()
		{
			delete m_pTextConverter;
		}

		CBinaryReader& CBinaryReader::operator >> (bool& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (char& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (unsigned char& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (signed char& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (short& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (unsigned short& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (int& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (unsigned int& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (long& value)
		{
			m_istr.read((char*) &value, sizeof(value));
		#if defined(JH_LONG_IS_64_BIT)
			if (m_flipBytes) value = CByteOrder::flipBytes((Int64) value);
		#else
			if (m_flipBytes) value = CByteOrder::flipBytes((Int32) value);
		#endif
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (unsigned long& value)
		{
			m_istr.read((char*) &value, sizeof(value));
		#if defined(JH_LONG_IS_64_BIT)
			if (m_flipBytes) value = CByteOrder::flipBytes((UInt64) value);
		#else
			if (m_flipBytes) value = CByteOrder::flipBytes((UInt32) value);
		#endif
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (float& value)
		{
			if (m_flipBytes)
			{
				char* ptr = (char*) &value;
				ptr += sizeof(value);
				for (unsigned i = 0; i < sizeof(value); ++i)
					m_istr.read(--ptr, 1);
			}
			else
			{
				m_istr.read((char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (double& value)
		{
			if (m_flipBytes)
			{
				char* ptr = (char*) &value;
				ptr += sizeof(value);
				for (unsigned i = 0; i < sizeof(value); ++i)
					m_istr.read(--ptr, 1);
			}
			else
			{
				m_istr.read((char*) &value, sizeof(value));
			}
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (Int64& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (UInt64& value)
		{
			m_istr.read((char*) &value, sizeof(value));
			if (m_flipBytes) value = CByteOrder::flipBytes(value);
			return *this;
		}

		CBinaryReader& CBinaryReader::operator >> (std::string& value)
		{
			UInt32 size = 0;
			read7BitEncoded(size);
			value.clear();
			if (!m_istr.good()) return *this;
			value.reserve(size);
			while (size--)
			{
				char c;
				if (!m_istr.read(&c, 1).good()) break;
				value += c;
			}
			if (m_pTextConverter)
			{
				std::string converted;
				m_pTextConverter->convert(value, converted);
				std::swap(value, converted);
			}
			return *this;
		}

		void CBinaryReader::read7BitEncoded(UInt32& value)
		{
			char c;
			value = 0;
			int s = 0;
			do
			{
				c = 0;
				m_istr.read(&c, 1);
				UInt32 x = (c & 0x7F);
				x <<= s;
				value += x;
				s += 7;
			}
			while (c & 0x80);
		}

		void CBinaryReader::read7BitEncoded(UInt64& value)
		{
			char c;
			value = 0;
			int s = 0;
			do
			{
				c = 0;
				m_istr.read(&c, 1);
				UInt64 x = (c & 0x7F);
				x <<= s;
				value += x;
				s += 7;
			}
			while (c & 0x80);
		}

		void CBinaryReader::readRaw(std::streamsize length, std::string& value)
		{
			value.clear();
			value.reserve(static_cast<std::string::size_type>(length));
			while (length--)
			{
				char c;
				if (!m_istr.read(&c, 1).good()) break;
				value += c;
			}
		}

		void CBinaryReader::readRaw(char* buffer, std::streamsize length)
		{
			m_istr.read(buffer, length);
		}

		void CBinaryReader::readBOM()
		{
			UInt16 bom;
			m_istr.read((char*) &bom, sizeof(bom));
			m_flipBytes = bom != 0xFEFF;
		}

		bool CBinaryReader::good()
		{
			return m_istr.good();
		}

		bool CBinaryReader::fail()
		{
			return m_istr.fail();
		}

		bool CBinaryReader::bad()
		{
			return m_istr.bad();
		}

		bool CBinaryReader::eof()
		{
			return m_istr.eof();
		}

		std::istream& CBinaryReader::stream() const
		{
			return m_istr;
		}

		CBinaryReader::StreamByteOrder CBinaryReader::byteOrder() const
		{
		#if defined(JH_ARCH_BIG_ENDIAN)
			return m_flipBytes ? LITTLE_ENDIAN_BYTE_ORDER : BIG_ENDIAN_BYTE_ORDER;
		#else
			return m_flipBytes ? BIG_ENDIAN_BYTE_ORDER : LITTLE_ENDIAN_BYTE_ORDER;
		#endif
		}
	}//end of namespace stream
}//end of namespace JHCPP
